//Steven Rychlik
//Gus Zarych
//Sarah Gray
//CPSC 315 Project 1

#include<iostream>
#include<string>
#include<vector>
#include<iomanip>
#include<sstream>
//#include "myDatabase.h"
using namespace std;

//class Database;
//string Database::parseWhere(string whereStmt, vector<string> relationNames, int relation1RowIndex, int relation2RowIndex);
//bool boolEvaluator(string parsedWhere);

const string trueStmt = "true=true";
const string falseStmt = "false=true";
unsigned int numViews = 0;
const string defaultPrimaryKey = "xDefaultx";

//don't forget to check primary keys for this data
class Relation {
private:
  string name;
  vector< vector<string> > myRelation;
  vector<string> types;
  vector<string> attributes;
  vector<string> primaryKey;
  vector<string> primaryKeyList;
  int numEntries;

  bool generatePrimaryKey(vector <string> row) {
    if (primaryKey[0] == defaultPrimaryKey) {
      string pk;
      stringstream stream;
      stream << numEntries;
      stream >> pk;
      primaryKeyList.push_back(pk);
      return true;
    } else {
      //get indexes of primary key columns
      vector<int> primaryKeyIndex;
      for (int i = 0; i < primaryKey.size(); i++) {
        for (int j = 0; j < attributes.size(); j++) {
          if (primaryKey[i] == attributes[j]) {
            primaryKeyIndex.push_back(j);
	  }
        }
      }
      //create primary key entry
      string primaryKeyEntry = "";
      for (int i = 0; i < primaryKeyIndex.size(); i++) {
        primaryKeyEntry = primaryKeyEntry + row[primaryKeyIndex[i]];
      }
      //check for repeat
      for (int i = 0; i < primaryKeyList.size(); i++) {
        if (primaryKeyList[i] == primaryKeyEntry)
          return false;
      }
      primaryKeyList.push_back(primaryKeyEntry);
      return true;
    }
  }

public:
  //Constructor
  Relation() {
    
  }

  Relation(string relationName, vector<string> attributeData, vector<string> attributeTypes, vector<string> primaryKeyData) {
    name = relationName;
    attributes = attributeData;
    types = attributeTypes;
    primaryKey = primaryKeyData;
    numEntries = 0;
  };

  //Access / Update functions
  void printRelation() {    //exclude primary key  
    //get column, put max field with into a vector for formatting purposes
    vector<int> fieldWidth;
    int currentWidth;
    int fieldWidthSum = 0;
    for (int i = 0; i < attributes.size(); i++) {
      vector<string> column = getColumn(attributes[i]);
      currentWidth = 0;
      for (int j = 0; j < column.size(); j++) {
        if (column[j].size() > currentWidth)
          currentWidth = column[j].size();
      }
      fieldWidth.push_back(currentWidth);
    }
    for (int i = 0; i < fieldWidth.size(); i++) {
      fieldWidthSum += fieldWidth[i] + 3;
    }
    //begin output
    cout << endl << setfill('-') << setw(fieldWidthSum) << "-"  << endl;
    cout << name << endl;
    cout << endl << setfill('-') << setw(fieldWidthSum) << "-"  << endl;
    for (int i = 0; i < attributes.size(); i++) {
      cout << setfill(' ') << setw(fieldWidth[i]) << attributes[i] << " | ";
    }
    cout << endl << setfill('-') << setw(fieldWidthSum) << "-" << endl;
    for (int i = 0; i < myRelation.size(); i++) {
      for (int j = 0; j < myRelation[i].size(); j++) {
        int padding = 0;
        if (j == 0) {
          padding = 0;
	} else {
          padding = 2;
      	}
        cout << setfill(' ') << setw(fieldWidth[j] + padding) << myRelation[i][j] << " ";
      }
      cout << endl;
    }
    cout << setfill('-') << setw(fieldWidthSum) << "-" << endl;
  };

  string getName() {
    return name;
  };

  vector<string> getAttributes() {
    return attributes;
  };

  vector<string> getTypes() {
    return types;
  }

  int getNumRows() {
    return myRelation.size();
  }

  vector<string> getRow(int rowIndex) {
    return myRelation[rowIndex];
  };

  vector<string> getColumn(string attribute) {
    //returns empty vector if incorrect attribute
    int index = 0;
    vector<string> column;
    for (int i = 0; i < attributes.size(); i++) {
      if (attributes[i] == attribute) {
        index = i;
      }
    }
    //if (index == 0) {  // for primary key
      //return column;
      //}
    //column.push_back(attribute);
    for (int i = 0; i < myRelation.size(); i++) {
      column.push_back(myRelation[i][index]);
    }
    return column;
  };

  //string insertData(vector<string> values) { //check for all values
  //  myRelation.push_back(values);
  //  return "ok";
  //};

  string insertData(vector<string> attributeList, vector<string> values) {
    if (attributeList.size() > attributes.size())
      return "Error: invalid input.\n";
    if (attributeList.size() != values.size())
      return "Error: invalid input.\n";
    //check attributeList versus attributes
    for (int i = 0; i < attributeList.size(); i++) {
      bool isValidAttr = false;
      for (int j = 0; j < attributes.size(); j++) {
        if (attributes[j] == attributeList[i])
          isValidAttr = true;
      }
      if (isValidAttr==false)
        return ("Error: Invalid attribute <" + attributeList[i] + ">");
    }

    vector<int> indexes;
    vector<string> row;
    for (int i = 0; i < attributes.size(); i++) {
      row.push_back("NULL");
    }
    for (int i = 0; i < attributeList.size(); i++) {
      for (int j = 0; j < attributes.size(); j++) {
        if (attributes[j] == attributeList[i]) {
	  indexes.push_back(j);
	}
      }
    }
    for (int i = 0; i < values.size(); i++) {
      row[indexes[i]] = values[i];
    }
    bool canInsert = generatePrimaryKey(row);
    if (canInsert) {
      myRelation.push_back(row);
      numEntries++;
    } else {
      return "Error: Data does not fit primary key.";
    }
    return "ok";
  };
  string updateData(vector<string> attributesToChange, vector<string> changesToMake, string whereConditional);

  string deleteData(string whereConditional) {
    return "ok";
  };

  string deleteAll() {
    myRelation.clear();
    return "ok";
  };
};

class Database {
public:
  //private:
  vector<Relation> myDatabase;
  
  string evalExists(string existsStmt) {
    //get string relationName from "EXISTS relationName"
    string relationName;
    string parameter = "EXISTS";
    int relationNameIndex = existsStmt.find_first_not_of(parameter) + 1;
    relationName = existsStmt.substr(relationNameIndex);
    //evaulate
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName)
        return trueStmt;
    }
    return falseStmt;
  }
 
  string evalIn(string evalInStmt, vector<string> relationNames, int rowIndex) {
    //parse evalInStmt into relationName, attrName, otherRelationName
    string relationName;
    string attrName;
    string otherRelationName;
    //statement is "relationName::attrName IN otherRelationName"
    string breakpoint = "::";
    int colonIndex = evalInStmt.find(breakpoint);
    int sizeOfRelationName = evalInStmt.find(breakpoint);
    relationName = evalInStmt.substr(0,sizeOfRelationName);
    breakpoint = "IN";
    int parameterIndex = evalInStmt.find(breakpoint);
    attrName = evalInStmt.substr((colonIndex + 2),(parameterIndex - 1)-(colonIndex + 2));
    otherRelationName = evalInStmt.substr(parameterIndex + 3);

    //get column of attrName from relationName
    vector<string> attrColumn;
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName) {
        attrColumn = myDatabase[i].getColumn(attrName);
      } 
    }
    //get column of otherRelation
    vector<string> otherColumn;
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == otherRelationName) {
        otherColumn = myDatabase[i].getColumn((myDatabase[i].getAttributes())[0]);
      }
    }
    //compare value of rowIndex in attrColumn to values in otherColumn
    for (int i = 0; i < otherColumn.size(); i++) {
      if ((attrColumn[rowIndex] != "NULL") && (otherColumn[i] != "NULL"))
        if (attrColumn[rowIndex] == otherColumn[i]) {
          return trueStmt;
      }
    }
    return falseStmt;
  }
  
  string evalAll(string evalAllStmt, vector<string> relationNames, int rowIndex) {
    //parse evalAllStmt into relationName, attrName, oper, otherRelationName
    string relationName;
    string attrName;
    string oper;
    string otherRelationName;
    //evalAllStmt = "relationName::attrName=ALL otherRelationName"
    string breakpoint = "::";
    int colonIndex = evalAllStmt.find(breakpoint);
    int sizeOfRelationName = evalAllStmt.find(breakpoint);
    relationName = evalAllStmt.substr(0,sizeOfRelationName);
    breakpoint = "<>=!";
    int operIndex = evalAllStmt.find_first_of(breakpoint);
    int sizeOfAttrName = operIndex - (colonIndex + 2);
    attrName = evalAllStmt.substr(colonIndex + 2, sizeOfAttrName);
    breakpoint = "ALL";
    int parameterIndex = evalAllStmt.find(breakpoint);
    oper = evalAllStmt.substr(operIndex, (parameterIndex - operIndex));
    otherRelationName = evalAllStmt.substr(parameterIndex + 4);

    //get the value to compare with (relationName [attr][row])
    string relationValue;
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName) {
        relationValue = myDatabase[i].getColumn(attrName)[rowIndex];
      }
    }
    
    //get the column of data from the other relation
    vector<string> otherColumn;
    for (int j = 0; j < myDatabase.size(); j++) {
      if (myDatabase[j].getName() == otherRelationName) {
        otherColumn = myDatabase[j].getColumn(myDatabase[j].getAttributes()[0]);
      }
    }    
    //if the operator is == just compare
    //else check, cast, compare
    if (oper == "=") {
      for (int i = 0; i < otherColumn.size(); i++) {
        if ((relationValue == "NULL") || (otherColumn[i] == "NULL"))
          return falseStmt;
        if (relationValue != otherColumn[i]) {
          return falseStmt;
	}
      }
      return trueStmt;
    } else if (oper == "!=") {
      for (int i = 0; i < otherColumn.size(); i++) {
        if ((relationValue == "NULL") || (otherColumn[i] == "NULL"))
          return falseStmt;
        if (relationValue == otherColumn[i]) {
          return falseStmt;
	}
      }
      return trueStmt;
    } else {
      if (relationValue == "NULL")
        return falseStmt;
      double relationValueD = atof(relationValue.c_str());
      //find operator code
      if (oper == "<") {
        for (int i = 0; i < otherColumn.size(); i++) {
          if (otherColumn[i] == "NULL")
            return falseStmt;
          if (relationValueD >= (atof(otherColumn[i].c_str())))
	    return falseStmt;
	}
        return trueStmt;
      }
      if (oper == ">") {
        for (int i = 0; i < otherColumn.size(); i++) {
          if (otherColumn[i] == "NULL")
            return falseStmt;
          if (relationValueD <= (atof(otherColumn[i].c_str())))
            return falseStmt;
        }
        return trueStmt;
      }
      if (oper == "<=") {
        for (int i = 0; i < otherColumn.size(); i++) {
          if (otherColumn[i] == "NULL")
            return falseStmt;
          if (relationValueD > (atof(otherColumn[i].c_str())))
            return falseStmt;
        }
        return trueStmt;
      }
      if (oper == ">=") {
        for (int i = 0; i < otherColumn.size(); i++) {
          if (otherColumn[i] == "NULL")
            return falseStmt;
          if (relationValueD < (atof(otherColumn[i].c_str())))
            return falseStmt;
        }
        return trueStmt;
      }
    }
    return falseStmt;
  }

  string evalAny(string evalAnyStmt, vector<string> relationNames, int rowIndex) {
    //parse evalAnyStmt into relationName, attrName, oper, otherRelationName
    string relationName;
    string attrName;
    string oper;
    string otherRelationName;
    //evalAnyStmt = "relationName::attrName=ANY otherRelationName"
    string breakpoint = "::";
    int colonIndex = evalAnyStmt.find(breakpoint);
    int sizeOfRelationName = evalAnyStmt.find(breakpoint);
    relationName = evalAnyStmt.substr(0,sizeOfRelationName);
    breakpoint = "<>=!";
    int operIndex = evalAnyStmt.find_first_of(breakpoint);
    int sizeOfAttrName = operIndex - (colonIndex + 2);
    attrName = evalAnyStmt.substr(colonIndex + 2, sizeOfAttrName);
    breakpoint = "ANY";
    int parameterIndex = evalAnyStmt.find(breakpoint);
    oper = evalAnyStmt.substr(operIndex, (parameterIndex - operIndex));
    otherRelationName = evalAnyStmt.substr(parameterIndex + 4);

    //get the value to compare with (relationName [attr][row])
    string relationValue;
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName) {
        relationValue = myDatabase[i].getColumn(attrName)[rowIndex];
      }
    }
    //get the column of data from the other relation
    vector<string> otherColumn;
    for (int j = 0; j < myDatabase.size(); j++) {
      if (myDatabase[j].getName() == otherRelationName) {
        otherColumn = myDatabase[j].getColumn(myDatabase[j].getAttributes()[0]);
      }
    }
    //if the operator is == just compare
    //else check, cast, compare
    if (oper == "=") {
      for (int i = 0; i < otherColumn.size(); i++) {
	if (otherColumn[i] == "NULL")
	  return falseStmt;
        if (relationValue == otherColumn[i]) {
          return trueStmt;
        }
      }
      return falseStmt;
    } else if (oper == "!=") {
      for (int i = 0; i < otherColumn.size(); i++) {
	if (otherColumn[i] == "NULL")
	  return falseStmt;
        if (relationValue != otherColumn[i]) {
          return trueStmt;
        }
      }
      return falseStmt;
    } else {
      if (relationValue == "NULL")
        return falseStmt;
      double relationValueD = atof(relationValue.c_str());
      //find operator code
      if (oper == "<") {
        for (int i = 0; i < otherColumn.size(); i++) {
          if (otherColumn[i] == "NULL")
            return falseStmt;
          if (relationValueD < (atof(otherColumn[i].c_str())))
            return trueStmt;
        }
        return falseStmt;
      }
      if (oper == ">") {
        for (int i = 0; i < otherColumn.size(); i++) {
          if (otherColumn[i] == "NULL")
            return falseStmt;
          if (relationValueD > (atof(otherColumn[i].c_str())))
            return trueStmt;
        }
        return falseStmt;
      }
      if (oper == "<=") {
        for (int i = 0; i < otherColumn.size(); i++) {
          if (otherColumn[i] == "NULL")
            return falseStmt;
          if (relationValueD <= (atof(otherColumn[i].c_str())))
            return trueStmt;
        }
        return falseStmt;
      }
      if (oper == ">=") {
        for (int i = 0; i < otherColumn.size(); i++) {
          if (otherColumn[i] == "NULL")
            return falseStmt;
          if (relationValueD >= (atof(otherColumn[i].c_str())))
            return trueStmt;
        }
        return falseStmt;
      }
    }
    return falseStmt;
  }

  string evalSum(string sumStmt, vector<string> relationNames) {
    //parsing relationName, attrName, and checking for valid cast
    string relationName;
    string attrName;
    //sumStmt = "SUM relationName::attrName"
    string breakpoint = "::";
    int colonIndex = sumStmt.find(breakpoint);
    relationName = sumStmt.substr(4, colonIndex - 4);
    attrName = sumStmt.substr(colonIndex + 2);
    //get data and check type
    vector<string> columnData;
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName) {
        vector<string> attributes = myDatabase[i].getAttributes();
        int attrIndex;
        for (int j = 0; j < attributes.size(); j++) {
          if (attributes[j] == attrName) {
            attrIndex = j;
	  }
	}
        vector<string> types = myDatabase[i].getTypes();
        if ((types[attrIndex] != "Int") && (types[attrIndex] != "Float"))
          return "Error: type cannot be summed.\n";
        columnData = myDatabase[i].getColumn(attrName);
      }
    }
    //evaluate Sum
    double sum = 0;
    for (int i = 0; i < columnData.size(); i++) {
      if (columnData[i] != "NULL")
        sum += atof(columnData[i].c_str());
    }
    string returnSum;
    stringstream stream;
    stream << sum;
    stream >> returnSum;
    return returnSum;
  } 

  string evalCount(string countStmt, vector<string> relationNames) {
    //parse for relationName, attrName
    string relationName;
    string attrName;
    //countStmt = "COUNT relationName::attrName"
    string breakpoint = "::";
    int colonIndex = countStmt.find(breakpoint);
    relationName = countStmt.substr(6, colonIndex - 6);
    attrName = countStmt.substr(colonIndex + 2);
    //get column
    vector<string> columnData;
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName) {
        columnData = myDatabase[i].getColumn(attrName);
      }
    }
    //find count;
    int count = 0;
    for (int i = 0; i < columnData.size(); i++) {
      if (columnData[i] != "NULL")
        count++;
    }
    string sCount;
    stringstream stream;
    stream << count;
    stream >> sCount;
    return sCount;
  }

  string evalMin(string minStmt, vector<string> relationNames) {
    //parse for relationName, attrName
    string relationName;
    string attrName;
    //minStmt = "MIN relationName::attrName"
    string breakpoint = "::";
    int colonIndex = minStmt.find(breakpoint);
    relationName = minStmt.substr(4, colonIndex - 4);
    attrName = minStmt.substr(colonIndex + 2);
    //get column
    vector<string> columnData;
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName) {
        vector<string> attributes = myDatabase[i].getAttributes();
        int attrIndex;
        for (int j = 0; j < attributes.size(); j++) {
          if (attributes[j] == attrName)
            attrIndex = j;
        }
        vector<string> types = myDatabase[i].getTypes();
        if (!((types[attrIndex] == "Int") || (types[attrIndex] == "Float")))
          return "Error: type cannot be orderd.\n";
        columnData = myDatabase[i].getColumn(attrName);
      }
    }
    //evaluate min
    int i = 0;
    while (columnData[i] == "NULL") i++;
    double min = atof(columnData[i].c_str());
    for (int j = i; j < columnData.size(); j++) {
      if (columnData[j] != "NULL")
        if (atof(columnData[j].c_str()) < min)
          min = atof(columnData[j].c_str());
    }
    //cast and return
    string sMin;
    stringstream stream;
    stream << min;
    stream >> sMin;
    return sMin;
  }

  string evalMax(string maxStmt, vector<string> relationNames) {
    //parse for relationName, attrName
    string relationName;
    string attrName;
    //maxStmt = "MAX relationName::attrName"
    string breakpoint = "::";
    int colonIndex = maxStmt.find(breakpoint);
    relationName = maxStmt.substr(4, colonIndex - 4);
    attrName = maxStmt.substr(colonIndex + 2);
    //get column
    vector<string> columnData;
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName) {
        vector<string> attributes = myDatabase[i].getAttributes();
        int attrIndex;
        for (int j = 0; j < attributes.size(); j++) {
          if (attributes[j] == attrName)
            attrIndex = j;
        }
        vector<string> types = myDatabase[i].getTypes();
        if (!((types[attrIndex] == "Int") || (types[attrIndex] == "Float")))
          return "Error: type cannot be ordered.\n";
        columnData = myDatabase[i].getColumn(attrName);
      }
    }
    //evaluate max
    int i = 0;
    while (columnData[i] == "NULL") i++;
    double max = atof(columnData[i].c_str());
    for (int j = i; j < columnData.size(); j++) {
      if (columnData[j] != "NULL")
        if (atof(columnData[j].c_str()) > max)
          max = atof(columnData[j].c_str());
    }
    //cast and return
    string sMax;
    stringstream stream;
    stream << max;
    stream >> sMax;
    return sMax;
  }
  
  string populateSingle(string stmt, vector<string> relationNames, int relation1RowIndex, int relation2RowIndex) {
    //parse for relationName and attrName
    string relationName;
    string attrName;
    string value = stmt;
    string colon = "::";
    int colonIndex = stmt.find(colon);
    relationName = stmt.substr(0,colonIndex);
    attrName = stmt.substr(colonIndex + 2);
    if (relationName == relationNames[0]) {
      for (int i = 0; i < myDatabase.size(); i++) {
        if (myDatabase[i].getName() == relationName) {
          value = myDatabase[i].getColumn(attrName)[relation1RowIndex];
          if (value == "NULL")
            return falseStmt;
	}
      }
    } else {
      for (int i = 0; i < myDatabase.size(); i++) {
        if (myDatabase[i].getName() == relationName) {
          value = myDatabase[i].getColumn(attrName)[relation2RowIndex];
          if (value == "NULL")
            return falseStmt;
        }
      }
    }
    return value;
  }

  string populateStatement(string stmt, int relation1RowIndex, int relation2RowIndex, vector<string> relationNames) {
    //populates a statement with data from the proper relations at row i (and j if two sources)
    string populatedStmt;
    string relationName1;
    string relationName2;
    string attrName1 = "NULL";
    string attrValue1;
    string attrName2 = "NULL";
    string attrValue2;
    string oper;
    //stmt = "relationName1::attr1<some operation>relationName2::attr2"
    string breakpoint = "<>=!"; //possible operator characters
    string::size_type operStartIndex = stmt.find_first_of(breakpoint);
    string::size_type operEndIndex = stmt.find_last_of(breakpoint);
    if (operStartIndex == string::npos) {
      return "Error: no operator in statement.\n";
    }
    oper = stmt.substr(operStartIndex, ((operEndIndex + 1) - operStartIndex));
    breakpoint = "::";
    string::size_type firstColonIndex = stmt.find(breakpoint);
    if (firstColonIndex != string::npos) { //found a relation::attr
      if (firstColonIndex < operStartIndex) { //relation::attr is before operator
        attrName1 = stmt.substr((firstColonIndex + 2), (operStartIndex - (firstColonIndex + 2)));
        relationName1 = stmt.substr(0,firstColonIndex);
      } else {                                          //relation is after operator
        attrName1 = stmt.substr((firstColonIndex + 2));
        relationName1 = stmt.substr((operEndIndex + 1), (firstColonIndex - (operEndIndex + 1)));
      }
    }
    string::size_type secondColonIndex = stmt.find(breakpoint, firstColonIndex + 1);
    if (secondColonIndex != string::npos) { //second colon will always be after operator
      attrName2 = stmt.substr((secondColonIndex + 2));
      relationName2 = stmt.substr((operEndIndex + 1), (secondColonIndex - (operEndIndex + 1)));
    }
    //find data
    //get attr1 column
    vector<string> attrColumn1;
    vector<string> attrColumn2;
    if (attrName1 != "NULL") {
      for (int i = 0; i < myDatabase.size(); i++) {
        if (myDatabase[i].getName() == relationName1) {
          attrColumn1 = myDatabase[i].getColumn(attrName1);
          attrValue1 = attrColumn1[relation1RowIndex];
          if (attrValue1 == "NULL")
            return falseStmt;
 	}
      }
    } else {                                                //first part is a literal
      attrValue1 = stmt.substr(0, operStartIndex);
    }
    //get attr2 column
    //cout << "Relation2:" << relationName2 << " AttrName2: " << attrName2 << endl;
    if (attrName2 != "NULL") {
      for (int i = 0; i < myDatabase.size(); i++) {
        if (myDatabase[i].getName() == relationName2) {
          attrColumn2 = myDatabase[i].getColumn(attrName2);
          if (relation2RowIndex == -1) {                    //statement has only 1 relation
            attrValue2 = attrColumn2[relation1RowIndex];
            if (attrValue2 == "NULL")
              return falseStmt;
	  } else {                                          //statement has 2 reations
            attrValue2 = attrColumn2[relation2RowIndex];
            if (attrValue2 == "NULL")
              return falseStmt;
	  }
        }
      }
    } else {                                                //second part is a literal
      attrValue2 = stmt.substr(operEndIndex + 1);
    }
    //cout << "attr Value 2: " << attrValue2 << endl;
    //generate result string from part 1, operator, and part 2 
    string part1;
    if (firstColonIndex < operStartIndex) { //part one is a replaced value
      part1 = attrValue1;
    } else {                               //part one is a literal
      part1 = stmt.substr(0,operStartIndex);
    }
    string part2;
    if (firstColonIndex > operStartIndex) {            //part two is a replaced value
      part2 = attrValue1;
    } else if (secondColonIndex > operStartIndex) {    //part two is value 2
      part2 = attrValue2;
    } else {                                           //part two is a literal
      part2 = stmt.substr(operEndIndex + 1);
    }
    populatedStmt = part1 + oper + part2;
    return populatedStmt;
  }
/*---------------------------------------------------------------------------------------------------*/
//Bool Evaluator

//ideally, this function will take any 3 parenthesis or less deep boolean funtion and evaluate it
//this function string has already been parsed and values to compare have already been put into it
// spaces have already been parsed out except in string being passed.

// determines if input character is valid in a string
bool isValidChar(char ch)
{

  if(ch== '.' || ch == ' ' || ch == '=')
    {
       return true;
    }
  return false;
}          

bool boolEvaluator(string function)
{
  int numParenthesis = 0;
  int tempIndex = 0;


  //calculate the depth of the boolean function
  while(function[tempIndex] == '(' || function[tempIndex] == '!')
    {
      if(function[tempIndex] == '(')
	{
	  numParenthesis++;
	}
      tempIndex++;
    }

  //Some 3 deep parenthesis cases start with 2 parenthesis and end with 3
  //inverseThreeDeep represents whether it is one of these cases
  bool inverseThreeDeep = false;
  int fSize = function.size();
  if(function[fSize-1]==')' && function[fSize-2]==')'&& function[fSize-3]==')')
    {
      inverseThreeDeep = true;
    }


  // finds the value of a 1 parenthesis deep boolean funtion
  if(numParenthesis == 1)
    {
      // function comes in form notFlag(arguementA operator1 arguementB)
      string arguementA;
      string arguementB;
      string operator1;
      
      //notFlag say whether or not there is a NOT value at begining of expression
      bool notFlag;
      notFlag = (function[0] == '!');
      //initial index value determined by notFlag
      int index;
      if(notFlag)
        index = 2;
      else
        index = 1;

      //find arguementA
      while((isalnum(function[index])!=0) || isValidChar(function[index]))
	{
          arguementA = arguementA + function[index];
	  index++;
        }  
      
      //find the operation symbol, it's either 1 or 2 chars
       while(isalnum(function[index])==0)
      	{
      	  operator1 = operator1 + function[index];
      	  index++;
      	}
     
     //find arguementB
     while(isalnum(function[index])!=0 || isValidChar(function[index]))
      {
	arguementB = arguementB + function[index];
	index++;
      }

     //evaluate the function and put it in value
      bool value;
      if(operator1 == "=")
	{
	  value = (arguementA == arguementB);
	}
      if(operator1 == "!=")
        {
          value = (arguementA != arguementB);
        }
      
      //if operation contains <,> the arguements are converted to floats then compared
      if(operator1 == ">=")
        {
          
	  float numericValueA = atof(arguementA.c_str());
	  float numericValueB = atof(arguementB.c_str());
          value = (numericValueA >= numericValueB);
        }
      
      if(operator1 == "<=")
        {

          float numericValueA = atof(arguementA.c_str());
          float numericValueB = atof(arguementB.c_str());
          value = (numericValueA <= numericValueB);
        }
      
      if(operator1 == ">")
        {

          float numericValueA = atof(arguementA.c_str());
          float numericValueB = atof(arguementB.c_str());
          value = (numericValueA >= numericValueB);
        }
      if(operator1 == "<")
        {

          float numericValueA = atof(arguementA.c_str());
          float numericValueB = atof(arguementB.c_str());
          value = (numericValueA < numericValueB);
        }
      if(notFlag)
	return !value;
      else
	return value;

    }

  // evaluates function value for 2 parenthesis deep expressions.
  if(numParenthesis == 2 && !inverseThreeDeep)
    {
      // function format is notFlag((expression1) boolOperation (expression2))
      string expression1;
      bool expression1Value;
      string expression2;
      bool expression2Value;
      string boolOperation;
      
      bool notFlag;
      notFlag = (function[0]=='!');
      int index;
      if(notFlag)
	index = 2;
      else
	index =1;

      //parse and evaluate expression1
      while(function[index] != ')')
	{
	  expression1 = expression1 + function[index];
	  index ++;
	}
      expression1 = expression1 + ")";
      expression1Value = boolEvaluator(expression1);
      index ++;

      
      //find the boolean operator ( ||, &&)
      for(int i =0; i<2; i++)
	{
	  boolOperation = boolOperation + function[index];
	  index++;
	}
      
      //parse and evaluate expression2
      while(function[index] != ')')
	{
	  expression2 = expression2 + function[index];
	  index++;
	}
      expression2 = expression2 + ")";
      expression2Value = boolEvaluator(expression2);

      
      bool value;
      //match boolOperation with ||, && for evaluation
      if(boolOperation == "||")
      {
	value = (expression1Value || expression2Value);
      }
  
      if(boolOperation == "&&")
      { 
        value = (expression1Value && expression2Value);
      }

      //not flag conditional
      if(notFlag)
        return !value;
      else
        return value;
    }
  
  //evaluates a 3 deep parenthesis function that is inverse
  if(numParenthesis == 2 && inverseThreeDeep)
    {
      //input in format notFlag((expression1))boolOperator((expression2)))
      //note- expression2 is actually 2 expressions that will be evaluated
      //recursively

      string expression1;
      string expression2;
      bool expression1Value;
      bool expression2Value;
      string boolOperation;

      bool notFlag;
      notFlag = (function[0]=='!');
      int index;
      if(notFlag)
        index = 2;
      else
        index =1;

      //parse and evaluate expression2
      while(function[index] != ')')
        {
          expression1 = expression1 + function[index];
          index++;
        }
      expression1 = expression1 + ")";
      expression1Value = boolEvaluator(expression1);
      index++;
      cout << expression1 <<  " expression 1 value is " << expression1Value << endl;

      
      //find the boolean operator ( ||, &&)
      for(int i =0; i<2; i++)
        {
          boolOperation = boolOperation + function[index];
          index++;
        }


      while(!(function[index-1] == ')' && function[index] == ')'))
        {
          expression2 = expression2 + function[index];
          index ++;
        }
      expression2 = expression2 + ")";
      expression2Value = boolEvaluator(expression2);
      cout << expression2 << " expression 2 value is " << expression2Value << endl;
      //expression value
      bool value;
      //match boolOperation with ||, && for evaluation
      if(boolOperation == "||")
        {
          value = (expression1Value || expression2Value);
        }

      if(boolOperation == "&&")
      {
        value = (expression1Value && expression2Value);
      }

      //not flag conditional
      if(notFlag)
        return !value;
      else
        return value;

 

    }
  
  // evaluates a 3 deep parenthesis function recursively 
  if(numParenthesis == 3)
    {
      //input in format notFlag(((expression1))boolOperator(expression2))
      //note- expression1 is actually 2 expressions that will be evaluated
      //recursively
      
      string expression1;
      string expression2;
      bool expression1Value;
      bool expression2Value;
      string boolOperation;
      
      bool notFlag;
      notFlag = (function[0]=='!');
      int index;
      if(notFlag)
        index = 2;
      else
        index =1;

      // find expression1 and evaluate
      while(!(function[index-1] == ')' && function[index] == ')'))
        {
          expression1 = expression1 + function[index];
          index ++;
        }
      expression1 = expression1 + ")";
      expression1Value = boolEvaluator(expression1);
      index ++;


      //find the boolean operator ( ||, &&)
      for(int i =0; i<2; i++)
        {
          boolOperation = boolOperation + function[index];
          index++;
        }

      //parse and evaluate expression2
      while(function[index] != ')')
        {
          expression2 = expression2 + function[index];
          index++;
        }
      expression2 = expression2 + ")";
      expression2Value = boolEvaluator(expression2);
      
      //expression value
      bool value;
      //match boolOperation with ||, && for evaluation
      if(boolOperation == "||")
	{
	  value = (expression1Value || expression2Value);
	}

      if(boolOperation == "&&")
      {
        value = (expression1Value && expression2Value);
      }

      //not flag conditional
      if(notFlag)
        return !value;
      else
        return value;

    }
}
/*----------------------------------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------------------------------*/
//Parse Where
// takes in partially parsed statements and outputs a form the boolEvaluator can use
//added relation name, and row indexes
string evaluateStatement(string statement, vector<string> relationNames, int relation1RowIndex, int relation2RowIndex)
{
  
  string::size_type findKeyword = statement.find("EXISTS ");
  if(findKeyword != string::npos)
    {
      return evalExists(statement);
    }
  
  findKeyword = statement.find("COUNT ");
  if(findKeyword != string::npos)
    {
      return evalCount(statement, relationNames);
    }
  
  findKeyword = statement.find("SUM ");
  if(findKeyword != string::npos)
    {
      return evalSum(statement, relationNames);
    }
  
  findKeyword = statement.find("MIN ");
  if(findKeyword != string::npos)
    {
      return evalMin(statement, relationNames);
    }

  findKeyword = statement.find("MAX ");
  if(findKeyword != string::npos)
    {
      return evalMax(statement, relationNames);
    }

  findKeyword = statement.find(" IN ");
  if(findKeyword != string::npos)
    {
      return evalIn(statement, relationNames, relation1RowIndex);
    }

  findKeyword = statement.find("ANY ");
  if(findKeyword != string::npos)
    {
      return evalAny(statement, relationNames, relation1RowIndex);
    }
  
  findKeyword = statement.find("ALL ");
  if(findKeyword != string::npos)
    {
      return evalAll(statement, relationNames, relation1RowIndex);
    }

  //BOOLEANS -- populate boolean values
  //does not execute
  findKeyword = statement.find("=");
  if(findKeyword != string::npos)
    {
      return "populateBool";
    }
  
  //does not execute
  findKeyword = statement.find("<");
  if(findKeyword != string::npos)
    {
      return "populateBool";
    }

  //does not execute
  findKeyword = statement.find(">");
  if(findKeyword != string::npos)
    {
      return "populateBool";
    }

  // if none of these are matched, it is assumed that it is a string literal or an attribute
  return populateSingle(statement, relationNames, relation1RowIndex, relation2RowIndex);
  

}

  // the main function that parses where conditionals
  // and prepares them to be evaluated in the bool evaluator
//added relationNames, relation1RowIndex, relation2RowIndex
string parseWhere(string whereCond, vector<string> relationNames, int relation1RowIndex, int relation2RowIndex)
{
  int numParenthesis = 0;
  int tempIndex = 0;

  //calculate the depth of the where conditional
  while(whereCond[tempIndex] == '(' || whereCond[tempIndex] == '!')
    {
      if(whereCond[tempIndex] == '(')
	{
	  numParenthesis++;
	}
      tempIndex++;
    }

  //cout << numParenthesis << endl;
  //check for inverse 3 deep function
  bool inverseThreeDeep = false;
  int fSize = whereCond.size();
  if(whereCond[fSize-1]==')' && whereCond[fSize-2]==')'&& whereCond[fSize-3]==')')
    {
      inverseThreeDeep = true;
    }

  //if epxression is 1 parenthesis deep
  if(numParenthesis == 1)
    {
      int index;
      if(whereCond[0]!= '!')
        index = 1;
      else
	index = 2;
      
      //used for putting the string back together after populating and filling
      int start1 = index;
      int endstmt1 = index+1;
      int start2 = -1;
      int endstmt2 = -1;

      //get condition without parenthesis around them, can be up to 2 
      string stmt1;
      //bool to flag whether it is a 2 stmt condition (ex: sum age > sum years) (Exists A)
      bool stmt2Flag = false;
      string stmt2 = "";
      while(whereCond.size()-1 > index)
	{
	  //check to see if this is a comparison operation, if it is, there are 2 expressions to find the value of
	  if((whereCond[index]== '<' || whereCond[index]== '>' || whereCond[index]== '!' || whereCond[index]== '=') && 
	     !((whereCond.substr(index+1, 3)== "ANY") || (whereCond.substr(index+1, 3)== "ALL") ||
	       (whereCond.substr(index+2, 3)== "ANY") || (whereCond.substr(index+2, 3)== "ALL"))) 
	    {
	      stmt2Flag = true;
	      endstmt1 = index;
	      //skip the comparison operator, it will be evaluated later
	      while(whereCond[index]== '<' || whereCond[index]== '>' || whereCond[index]== '!' || whereCond[index]== '=')
		{
		  index++;
		}
	      start2 = index;
	    }
	 
	  if(stmt2Flag == false)
	    {
	      stmt1+= whereCond[index];
	    }
	  else
	    {
	      stmt2+= whereCond[index];
	    }
	  index++;
	}
      // eval 2nd stmt if there is 1
      string filledConditional1 = evaluateStatement(stmt1, relationNames, relation1RowIndex, relation2RowIndex);
      string filledConditional2 = "unused";
      if(stmt2!="")
	{
	  filledConditional2 = evaluateStatement(stmt2, relationNames, relation1RowIndex, relation2RowIndex);
	}
      
      //put whereCond back together with populated variables
      string filledConditional;
      if(filledConditional2 != "unused")
	{
	  filledConditional = whereCond.substr(0,start1) + filledConditional1 +  whereCond.substr(endstmt1, start2 - endstmt1) + filledConditional2 + ')';
	}
     
      else
	{
	  filledConditional = whereCond.substr(0,start1) + filledConditional1 + ')';
	}
      //cout << "stmt1: " << stmt1 << endl << "stmt2: " << stmt2 << endl;
      return filledConditional; 
    }

  //parse a where statment of 2 parenthesis deep
  if((numParenthesis == 2) && (inverseThreeDeep == false))
    {
      int index = 1;
      while(whereCond[index+1] == '!' || whereCond[index+1] == '(')
	{
	  index++;
	}

      //variables to keep track of where each conditional begins/ends 
      int start1 = index;
      int start2 = 0;
      int endCond1 = 0;
      int endCond2 = 0;
      //expressions used and evaluated
      string expression1 = "";
      string expression2 = "";
      string filledConditional1 = "";
      string filledConditional2 = "";
      while(whereCond[index]!= ')')
	{
	  expression1 += whereCond[index];
	  index++;
	}
      expression1 += whereCond[index];
      //cout << "expression1: " << expression1 << endl;

      filledConditional1 = parseWhere(expression1, relationNames, relation1RowIndex, relation2RowIndex);
      index++;
      endCond1 = index;
      
      //parse past possible not flags, and operator to get 2nd conditional
      while(whereCond[index] != '(')
        {
          index++;
        }
      //index++;
      start2 = index;
      while(whereCond[index]!= ')')
        {
          expression2 += whereCond[index];
          index++;
        }
      index++;
      expression2 += whereCond[index];
      endCond2 = index;
      //cout << "expression2: " << expression2 << endl;

      filledConditional2 = parseWhere(expression2, relationNames, relation1RowIndex, relation2RowIndex);
      
      //concatenate the filled in values
      string filledOverallConditional;
      /*cout << whereCond.substr(0, start1) << endl;
      cout << filledConditional1 << endl;
      cout << whereCond.substr(endCond1, start2 - endCond1) << "2nd " << start2 - endCond1 << endl;
      cout << filledConditional2 << endl;
      cout << whereCond.substr(endCond2, whereCond.size()-endCond2) << endl; */
      filledOverallConditional = whereCond.substr(0, start1) + filledConditional1 + whereCond.substr(endCond1, start2 - endCond1) + 
	filledConditional2 + whereCond.substr(endCond2, whereCond.size()-endCond2);
      
      return filledOverallConditional;
           
    }  
  
  //3 parenthesis inverse statement
  if((numParenthesis == 2) && (inverseThreeDeep == true))
    {
      int index = 1;
      while(whereCond[index+1] == '!' || whereCond[index+1] == '(')
        {
          index++;
        }

      //variables to keep track of where each conditional begins/ends
      int start1 = index;
      int start2 = 0;
      int endCond1 = 0;
      int endCond2 = 0;
      //expressions used and evaluated
      string expression1 = "";
      string expression2 = "";
      string filledConditional1 = "";
      string filledConditional2 = "";
      while(whereCond[index]!= ')')
        {
          expression1 += whereCond[index];
          index++;
        }
      expression1 += whereCond[index];
      // cout << "expression1: " << expression1 << endl;
      
      filledConditional1 = parseWhere(expression1, relationNames, relation1RowIndex, relation2RowIndex);
      index++;
      endCond1 = index;

      //parse past possible not flags, and operator to get 2nd conditional
      while(whereCond[index] != '(')
        {
          index++;
        }
 
      start2 = index;
      //keeps track of ')' found, looking for 2 before the end of the statement
      int count= 0;
      while(whereCond[index]!= ')' || count <= 2)
        {
	  if(whereCond[index]== ')')
	    {
	      count ++;
	    }
          expression2 += whereCond[index];
          index++;
        }
      //get the two parenthesis
      index++;
      expression2 += whereCond[index];
      endCond2 = index;
      index++;
      expression2 += whereCond[index];

      //cout << "expression2: " << expression2 << endl;

      filledConditional2 = parseWhere(expression2, relationNames, relation1RowIndex, relation2RowIndex);

      //concatenate the filled in values
      string filledOverallConditional;
      /* cout << whereCond.substr(0, start1) << endl;
      cout << filledConditional1 << endl;
      cout << whereCond.substr(endCond1, start2 - endCond1) << endl;
      cout << filledConditional2 << endl;
      cout << "line 311 " << whereCond.size() << " " << endCond2 << endl;
      cout << whereCond.substr(endCond2, 1) << endl;*/
      filledOverallConditional = whereCond.substr(0, start1) + filledConditional1 + whereCond.substr(endCond1, start2 - endCond1) +
        filledConditional2 + ')';

      return filledOverallConditional;

      
    }


  if(numParenthesis == 3) 
    {
      int index;
      if(whereCond[0] != '!')
	  {
	    index =1;
	  }
	 else
	  {
	    index = 2;
	  }
	   

      //variables to keep track of where each conditional begins/ends
      int start1 = index;
      int start2 = 0;
      int endCond1 = 0;
      int endCond2 = 0;
      //expressions used and evaluated, expression1 actually 2 axpressions
      string expression1 = "";
      string expression2 = "";
      string filledConditional1 = "";
      string filledConditional2 = "";
      
      int count = 0;
      //find first expression
      while(whereCond[index] != ')' || count <= 3)
	{
	  if(whereCond[index] == ')')
	    {
	      count++;
	    }
	  if(count == 3)
	    {
	      expression1 += whereCond[index];
	      index++;
	      break;
	    }
	  
	  expression1 += whereCond[index];
	  index++;
      
	}      
      //cout << "exrpession1 in 3 deep: " << expression1 << endl;
      filledConditional1 = parseWhere(expression1, relationNames, relation1RowIndex, relation2RowIndex);
      endCond1 = index;

      while(whereCond[index] != '(')
	{
	  index++;
	}
      start2 = index;
      //find 2nd epxression
      while(whereCond[index] != ')')
	{
	  expression2 += whereCond[index];
	  index++;
	}

      index++;
      expression2 += whereCond[index];
      endCond2 = index;
      //cout << "exrpession2 in 3 deep: " << expression2 << endl;
      filledConditional2 = parseWhere(expression2, relationNames, relation1RowIndex, relation2RowIndex);

      //concatinate filled strings
      string filledOverallConditional;
      /*cout << whereCond.substr(0, start1) << endl;
      cout << filledConditional1 << endl;
      cout << whereCond.substr(endCond1, start2 - endCond1) << endl;
      cout << filledConditional2 << endl;
      cout << "line 311 " << whereCond.size() << " " << endCond2 << endl;
      cout << whereCond.substr(endCond2, 1) << endl;*/
      filledOverallConditional = whereCond.substr(0, start1) + filledConditional1 + whereCond.substr(endCond1, start2 - endCond1) +
        filledConditional2 + ')';

      return filledOverallConditional;
    }
}
/*----------------------------------------------------------------------------------------------------*/
  //public:
  //Database functions
  Database() {};
  vector<string> getRelationNames() {
    vector<string> names;
    for (int i = 0; i < myDatabase.size(); i++) {
      names.push_back(myDatabase[i].getName());
    }
    return names;
  }
  vector<string> getAttributes(string relationName) {
    for (int i = 0 ;i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName)
        return myDatabase[i].getAttributes();
    }
  }
  vector< vector<string> > getRelationAndAttributeNames() {
    vector< vector<string> > names;
    for (int i = 0; i < myDatabase.size(); i++) {
      vector<string> relationAndAttributes;
      vector<string> attributes;
      attributes = myDatabase[i].getAttributes();
      relationAndAttributes.push_back(myDatabase[i].getName());
      for (int j = 0; j < attributes.size(); j++) {
        relationAndAttributes.push_back(attributes[j]);
      }
      names.push_back(relationAndAttributes);
    }
    return names;
  }
  
  //Relation linked functions
  string printRelation(string relationName) {
    for (int i = 0; i < myDatabase.size(); i++) {
      if (relationName == myDatabase[i].getName()) {
        myDatabase[i].printRelation();
        return "ok";
      }
    }
    return "Error: no relation found";
  }
  string createRelation(string relationName, vector<string> attributeNames, vector<string> attributeTypes, vector<string> primaryKey) {
    if (myDatabase.size() > 0) {
      for (int i = 0; i < myDatabase.size(); i++) {
        if (myDatabase[i].getName() == relationName)
          return "Error: duplicate relation name";
      }
    }
    //type checking
    for (int i = 0; i < attributeTypes.size(); i++) {
      if (!((attributeTypes[i] == "String") || (attributeTypes[i] == "Int") || (attributeTypes[i] == "Float")))
        return ("Error: Invalid type <" + attributeTypes[i] + ">.");
    }
    Relation r(relationName, attributeNames, attributeTypes, primaryKey);
    myDatabase.push_back(r);
    return "ok";
  }
  string insertData(string relationName, vector<string> attributeList, vector<string> values) {
    // If attribute list is empty, values is a full list of data (or Nulls at the end of the table)
    for (int i = 0; i < myDatabase.size(); i++) {
      if (relationName == myDatabase[i].getName()) {
        return myDatabase[i].insertData(attributeList, values);
      }
    }
    return "Error: no relation found";
  }
  string updateData(string relationName, vector<string> attributes, vector<string> changes, string whereConditional) {
    for (int i = 0; i < myDatabase.size(); i++) {
      if (relationName == myDatabase[i].getName()) {
        return myDatabase[i].updateData(attributes, changes, whereConditional);
      }
    }
    return "Error: no relation found";
  }
  string deleteAll(string relationName) {
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName) {
        return myDatabase[i].deleteAll();
      }
    }  
    return "Error: no relation found";
  }
  string deleteData(string relationName, string whereConditional) {
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName) {
        return myDatabase[i].deleteData(whereConditional);
      }
    }
    return "Error: no relation found";
  }
  string deleteRelation(string relationName) {
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName) {
        myDatabase.erase(myDatabase.begin() + i);
        return "ok";
      }
    }
    return "Error: no relation found";
  }
  
  bool isRelation(string relationName) {
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName)
        return true;
    }
    return false;
  }

  bool isAttribute(string relationName, string attrName) {
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName) {
        vector<string> attrColumn = myDatabase[i].getAttributes();
        for (int j = 0; j < attrColumn.size(); j++) {
          if (attrName == attrColumn[j])
            return true;
		}
      }
    }
    return false;
  }
  
  string numEntriesInRelation(string relationName) {
    for (int i = 0; i < myDatabase.size(); i++) {
      if (myDatabase[i].getName() == relationName) {
        int num = myDatabase[i].getNumRows();
        string numStr;
        stringstream stream;
        stream << num;
        stream >> numStr;
        return numStr;
      }
    }
    return "Error: No Relation Found";
  }
  
  string numRelations() {
	  stringstream stream;
	  stream << myDatabase.size();
	  string number;
	  stream >> number;
	  return number;
  }

  string numEntriesInDatabase() {
    int num = 0;
    for (int i = 0; i < myDatabase.size(); i++) {
      num += myDatabase[i].getNumRows();
    }
    string numStr;
    stringstream stream;
    stream << num;
    stream >> numStr;
    return numStr;
  }

  string query(vector<string> relationNames, vector<string> attributeNames, vector<string> attributeAliases, string whereConditional) {
    //create view table
    stringstream stream;
    string numViewsStr;
    stream << numViews;
    stream >> numViewsStr;
    string viewName = "View " + numViewsStr;
    vector<string> viewAttrNames;
    vector<string> viewAttrTypes;
    if (attributeNames[0] == "*") {
      for (int i = 0; i < attributeNames.size(); i++) {    //get attribute types
        string colon = "::";
        int colonIndex = attributeNames[i].find(colon);
        string relationName = attributeNames[i].substr(0, colonIndex);
        //string attrName = attributeNames[i].substr(colonIndex + 2);
        for (int j = 0; j < myDatabase.size(); j++) {
          if (myDatabase[j].getName() == relationName) {
            vector<string> attributeList = myDatabase[j].getAttributes();
            vector<string> typeList = myDatabase[j].getTypes();
            for (int k = 0; k < attributeList.size(); k++) {
              viewAttrTypes.push_back(typeList[k]);
            }
          }
        }
      }
      for (int i = 0; i < relationNames.size(); i++) {
        string name = relationNames[i];
        vector<string> relationAttributes = getAttributes(relationNames[i]);
        for (int j = 0; j < relationAttributes.size(); j++) {
          viewAttrNames.push_back(name + "::" + relationAttributes[j]);
	}
      }

    } else {
      for (int i = 0; i < attributeNames.size(); i++) {    //get attribute types
        string colon = "::";
        int colonIndex = attributeNames[i].find(colon);
        string relationName = attributeNames[i].substr(0, colonIndex);
        string attrName = attributeNames[i].substr(colonIndex + 2);
        for (int j = 0; j < myDatabase.size(); j++) {
          if (myDatabase[j].getName() == relationName) {
            vector<string> attributeList = myDatabase[j].getAttributes();
            vector<string> typeList = myDatabase[j].getTypes();
            for (int k = 0; k < attributeList.size(); k++) {
              if (attributeList[k] == attrName) {
                viewAttrTypes.push_back(typeList[k]);
	      }
	    }
	  }
        }
      }
    
      for (int i = 0; i < attributeAliases.size(); i++) {
        if (attributeAliases[i] != "NULL") {
          string thisRelationName;
          int colonIndex = attributeNames[i].find("::");
          thisRelationName = attributeNames[i].substr(0, colonIndex);
          viewAttrNames.push_back(thisRelationName + "::" + attributeAliases[i]);
        }
        else
          viewAttrNames.push_back(attributeNames[i]);
      }
    }
    vector<string> viewPrimaryKey;
    viewPrimaryKey.push_back(defaultPrimaryKey);
    string result = createRelation(viewName, viewAttrNames, viewAttrTypes, viewPrimaryKey);
 
    //get relation(s)
    if (relationNames.size() < 1) 
      cerr << "Error: invalid relation list.\n";
    if (relationNames.size() > 2)
      cerr << "Error: too many relations in From clause.\n";
    if (relationNames.size() == 2) {
      Relation relation1, relation2;
      for (int i = 0; i < myDatabase.size(); i++) {
        if (myDatabase[i].getName() == relationNames[0])
          relation1 = myDatabase[i];
        if (myDatabase[i].getName() == relationNames[1])
          relation2 = myDatabase[i];
      }
      for (int i = 0; i < relation1.getNumRows(); i++) {
        for (int j = 0; j < relation2.getNumRows(); j++) {
          //parse where
          //bool evaluate
	}
      }
    }
    if (relationNames.size() == 1) {
      Relation relation1;
      for (int i = 0; i < myDatabase.size(); i++) {
        if (myDatabase[i].getName() == relationNames[0])
          relation1 = myDatabase[i];
      }
      //get attribute indexes
      vector<int> relation1Indexes;
      vector<string> relation1Attributes = relation1.getAttributes();
      for (int j = 0; j < attributeNames.size(); j++) {
        for (int k = 0; k < relation1Attributes.size(); k++) {
          string attrName;
          int colonIndex = attributeNames[j].find("::");
          attrName = attributeNames[j].substr(colonIndex + 2);
          if (attrName == relation1Attributes[k])
            relation1Indexes.push_back(k);
	}
      }
      
      for (int i = 0; i < relation1.getNumRows(); i++) {
        //parse where
        string parsedWhere = parseWhere(whereConditional, relationNames, i, -1);
        //bool evaluate 
        bool isValid = boolEvaluator(parsedWhere);
        if (isValid) {
	  //insert into view
          vector<string>values;
          string attrName;
          for (int j = 0; j < viewAttrNames.size(); j++) {
	      int colonIndex = viewAttrNames[j].find("::");
	      attrName = viewAttrNames[j].substr(colonIndex + 2);
              vector<string> attrColumn = relation1.getColumn(attrName);
              values.push_back(attrColumn[i]); 
	  }
            string end = insertData(viewName, viewAttrNames, values);
				
	}
      }
      
    }

     return "ok";
  }  
};

string Relation::updateData(vector<string> attributesToChange, vector<string> changesToMake, string whereConditional) {
  /*  //get attribute indexes
  vector<int> attributeIndexes;
  for (int i = 0; i < attributesToChange.size(); i++) {
    for (int j = 0; j < attributes.size(); j++) {
      if (attributesToChange[i] == attributes[j])
	attributeIndexes.push_back(j);
    }
  }
  //evaluate where
  vector<string> relationNames;
  relationNames.push_back(name);
  for (int i = 0; i < myRelation.size(); i++) {
    string parsedWhere = parseWhere(whereConditional, relationNames, i, (int)-1);
    bool isValid = boolEvaluator(parsedWhere);
    if (isValid) {
      for (int j = 0; j < attributeIndexes.size(); ++j) {
	myRelation[i][(attributeIndexes[j])] = changesToMake[j];
      }
    }
    }*/
  return "ok";
}

